package MultithreadCase;


//单例模式：能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例

//饿汉模式：都是线程安全的
class Singleton {
    //1、使用 static 创建一个实例，并且立即进行实例化.这个instance对应的实例就是该类的唯一实例
    private static Singleton instance = new Singleton();//类加载阶段就会直接创建实例(程序中用到该类就会立即加载)

    //2、把构造方法设置为私有的，防止其他地方对 Singleton 进行实例化
    private Singleton() {

    }

    //3、提供一个方法，让外面能够拿到唯一实例
    public static Singleton getinstance() {
        return instance;
    }
}

//饿汉模式中，getinstance仅仅是读取了变量的内容，当多个线程只是读取一个变量的时候也是线程安全的


//懒汉模式-多线程不安全版本
class Singleton1 {
    //1、不是立即就初始化实例
    private static Singleton1 instance = null;

    //2、将构造方法设置为私有的
    private Singleton1() {

    }

    //3、提供一个方法来获取实例.只有当我们真正用到这个实例的时候才会去创建这个 实例
    public static Singleton1 getInstance() {
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }
}

//而在懒汉模式中，getinstance既包含了读，又包含了修改。而且读和修改还不是原子的，所以并不是线程安全的，因此也会创建出多个实例

//那如何处理呢？ 直接加锁synchronized(类对象)，类对象只有一份，能保证多个线程调用getinstance时是针对同一个对象进行的

/*加完锁，线程安全的问题是解决了。但是又会有新问题：线程不安全是发生在instance被初始化之前的，这时调用getinstance会涉及到读和修改！
而一旦instance被初始化后，就只有两个读操作了，此时线程安全！而上述加锁方式，不管你是否对instance进行初始化，只要调用getinstance就会进行加锁，
造成了很多不必要的 锁竞争！所以我们的改进方案就是让instance初始化之前才加锁，只需要在加锁这里加上一层条件判定即可(当前是否已经初始化)*/

/*以上解决后还存在一个问题，当多个线程调用getinstance时，外层if中会造成大量的读instance内存操作，可能会让编译器优化为读寄存器。一旦优化，
后续线程进行修改instance后，紧接着的线程都感知不到这个修改，还是把instance判断为null，那么外层if就失效了！所以处理方法就是给instance加上volatile*/

//改正：上面的volatile并不是为了解决内存可见性问题，而最大的用处是 禁止指令重排序 。instance = new Singleton2();分为三个步骤依次为：1.创建内存 2.针对内存进行初始化3.把内存的地址赋值给引用

//懒汉模式整体优化如下：
class Singleton2 {
    private static volatile Singleton2 instance = null;

    private Singleton2() {

    }

    //注意：以下两个if中的条件并不一样，这里只是巧合
    public static Singleton2 getInstance() {
        //判断是否初始化，若没有初始化就存在线程不安全，得加锁
        if (instance == null) {
            synchronized (Singleton2.class) {
                if (instance == null) {//判断是否要创建实例
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }



    public static class SingletonPattern {
        public static void main1(String[] args) {
            Singleton instance = Singleton.getinstance();

            //Singleton instance2 = new Singleton();  由于前面的私有构造方法，这里就实例化不了
        }


        public static void main2(String[] args) {
            Singleton1 instance = Singleton1.getInstance();
        }

        public static void main3(String[] args) {
            Singleton2 instance = Singleton2.getInstance();
        }
    }
}
